import Database from 'better-sqlite3';
import {DbMetadataUtils} from '../common';

/**
 * 查询计划节点类型
 */
export interface QueryPlanNode {
  /**
   * 计划ID
   */
  id: number;
  /**
   * 父计划ID
   */
  parentId: number | null;
  /**
   * 详细说明
   */
  detail: string;
}

/**
 * 查询分析结果
 */
export interface QueryAnalysisResult {
  /**
   * SQL语句
   */
  sql: string;
  /**
   * 查询执行计划
   */
  queryPlan: QueryPlanNode[];
  /**
   * 执行时间（毫秒）
   */
  executionTime: number;
  /**
   * 性能建议
   */
  suggestions: string[];
}

/**
 * SQL查询分析器类
 * 提供分析查询性能、生成优化建议和索引推荐的功能
 *
 * 兼容 better-sqlite3 v11.8.1 及 SQLite 3.48.0
 */
export class QueryAnalyzer {
  /**
   * 分析SQL查询
   * @param db 数据库实例
   * @param sql SQL查询语句
   * @param params 查询参数
   * @returns 分析结果
   */
  public static analyze(
    db: Database.Database,
    sql: string,
    params: any[] = []
  ): QueryAnalysisResult {
    // 开启分析选项
    db.pragma('analysis_limit = 1000');

    // 获取查询计划
    const explainQuery = `EXPLAIN QUERY PLAN ${sql}`;
    const planStmt = db.prepare(explainQuery);
    const queryPlan = planStmt.all(...params) as QueryPlanNode[];

    // 计时执行查询
    const startTime = Date.now();

    // 实际执行查询
    db.prepare(sql).all(...params);

    const executionTime = Date.now() - startTime;

    // 分析查询计划，提供优化建议
    const suggestions = this.analyzePlan(queryPlan, sql);

    return {
      sql,
      queryPlan,
      executionTime,
      suggestions
    };
  }

  /**
   * 分析查询计划，提供优化建议
   * @param queryPlan 查询计划
   * @param sql 原始SQL
   * @returns 优化建议
   */
  private static analyzePlan(queryPlan: QueryPlanNode[], sql: string): string[] {
    const suggestions: string[] = [];

    // 检查是否使用了索引
    const usesIndex = queryPlan.some(p =>
      p.detail.includes('USING INDEX') ||
      p.detail.includes('USING COVERING INDEX')
    );

    if (!usesIndex) {
      suggestions.push('查询未使用索引，请考虑为相关列添加索引以提高查询性能。');
    }

    // 检查表扫描
    const hasFullScan = queryPlan.some(p =>
      p.detail.includes('SCAN TABLE') &&
      !p.detail.includes('USING INDEX')
    );

    if (hasFullScan) {
      suggestions.push('检测到全表扫描，这在大表上可能导致性能问题。');
    }

    // 检查临时表使用
    const usesTempTable = queryPlan.some(p =>
      p.detail.includes('USE TEMP') ||
      p.detail.includes('TEMP TABLE')
    );

    if (usesTempTable) {
      suggestions.push('查询使用了临时表，可能影响性能，尤其是处理大量数据时。');
    }

    // 检查ORDER BY或GROUP BY
    if (sql.toUpperCase().includes('ORDER BY')) {
      const hasIndexForSort = queryPlan.some(p =>
        p.detail.includes('USING INDEX') &&
        p.detail.includes('SCAN') &&
        p.detail.includes('ORDER BY')
      );

      if (!hasIndexForSort && sql.toUpperCase().includes('ORDER BY')) {
        suggestions.push('ORDER BY操作没有使用索引，请考虑为排序列添加索引。');
      }
    }

    // 检查JOIN操作
    const hasJoin = queryPlan.some(p =>
      p.detail.includes('JOIN')
    );

    if (hasJoin) {
      const hasIndexForJoin = queryPlan.some(p =>
        p.detail.includes('JOIN') &&
        p.detail.includes('USING INDEX')
      );

      if (!hasIndexForJoin) {
        suggestions.push('JOIN操作没有使用索引，请考虑为连接条件的列添加索引。');
      }
    }

    // 如果没有发现明显问题
    if (suggestions.length === 0) {
      suggestions.push('查询计划良好，未发现明显的性能问题。');
    }

    return suggestions;
  }

  /**
   * 查找查询中使用的表名
   * @param sql SQL查询语句
   * @returns 表名数组
   */
  public static extractTableNames(sql: string): string[] {
    // 简单实现，可能不适用于复杂查询
    const fromRegex = /\bFROM\s+([^\s,()]+)/gi;
    const joinRegex = /\bJOIN\s+([^\s,()]+)/gi;

    const tables: string[] = [];

    // 找出FROM子句中的表
    let match;
    while ((match = fromRegex.exec(sql)) !== null) {
      if (match[1] && !match[1].toUpperCase().startsWith('SELECT')) {
        tables.push(match[1].replace(/[\[\]"`']/g, ''));
      }
    }

    // 找出JOIN子句中的表
    while ((match = joinRegex.exec(sql)) !== null) {
      if (match[1]) {
        tables.push(match[1].replace(/[\[\]"`']/g, ''));
      }
    }

    // 移除重复表名
    return [...new Set(tables)];
  }

  /**
   * 生成可能有用的索引建议
   * @param db 数据库实例
   * @param sql SQL查询语句
   * @returns 索引建议
   */
  public static suggestIndices(
    db: Database.Database,
    sql: string
  ): string[] {
    // 提取表名
    const tableNames = this.extractTableNames(sql);

    // 将SQL转为大写便于分析
    const uppercaseSql = sql.toUpperCase();
    const suggestions: string[] = [];

    // 对每个表分析潜在的索引列
    for (const tableName of tableNames) {
      // 获取表的列信息
      const columns = DbMetadataUtils.getTableColumns(db, tableName);

      // 获取已有索引
      const existingIndices = DbMetadataUtils.getTableIndices(db, tableName);

      const existingIndexColumns: string[] = [];

      // 获取每个索引覆盖的列
      for (const idx of existingIndices) {
        const indexColumns = DbMetadataUtils.getIndexColumns(db, idx.name);
        existingIndexColumns.push(...indexColumns.map(c => c.name));
      }

      // 检查WHERE子句中的列
      const wherePattern = new RegExp(`\\bWHERE\\b[^()]*\\b${tableName}\\.([\\w_]+)\\b`, 'gi');
      let whereMatch;

      while ((whereMatch = wherePattern.exec(sql)) !== null) {
        const columnName = whereMatch[1];
        const column = columns.find(c => c.name.toLowerCase() === columnName.toLowerCase());

        if (column && !existingIndexColumns.includes(column.name) && column.pk === 0) {
          suggestions.push(`考虑在表 ${tableName} 的列 ${column.name} 上创建索引，它在WHERE子句中被使用。`);
        }
      }

      // 检查JOIN条件中的列
      const joinPattern = new RegExp(`\\bJOIN\\b[^()]*\\bON\\b[^()]*\\b${tableName}\\.([\\w_]+)\\b`, 'gi');
      let joinMatch;

      while ((joinMatch = joinPattern.exec(sql)) !== null) {
        const columnName = joinMatch[1];
        const column = columns.find(c => c.name.toLowerCase() === columnName.toLowerCase());

        if (column && !existingIndexColumns.includes(column.name) && column.pk === 0) {
          suggestions.push(`考虑在表 ${tableName} 的列 ${column.name} 上创建索引，它在JOIN条件中被使用。`);
        }
      }

      // 检查ORDER BY子句中的列
      if (uppercaseSql.includes('ORDER BY')) {
        const orderByPattern = new RegExp(`\\bORDER\\s+BY\\b[^()]*\\b${tableName}\\.([\\w_]+)\\b`, 'gi');
        let orderByMatch;

        while ((orderByMatch = orderByPattern.exec(sql)) !== null) {
          const columnName = orderByMatch[1];
          const column = columns.find(c => c.name.toLowerCase() === columnName.toLowerCase());

          if (column && !existingIndexColumns.includes(column.name) && column.pk === 0) {
            suggestions.push(`考虑在表 ${tableName} 的列 ${column.name} 上创建索引，它在ORDER BY中被使用。`);
          }
        }
      }

      // 检查GROUP BY子句中的列
      if (uppercaseSql.includes('GROUP BY')) {
        const groupByPattern = new RegExp(`\\bGROUP\\s+BY\\b[^()]*\\b${tableName}\\.([\\w_]+)\\b`, 'gi');
        let groupByMatch;

        while ((groupByMatch = groupByPattern.exec(sql)) !== null) {
          const columnName = groupByMatch[1];
          const column = columns.find(c => c.name.toLowerCase() === columnName.toLowerCase());

          if (column && !existingIndexColumns.includes(column.name) && column.pk === 0) {
            suggestions.push(`考虑在表 ${tableName} 的列 ${column.name} 上创建索引，它在GROUP BY中被使用。`);
          }
        }
      }
    }

    // 移除重复建议
    return [...new Set(suggestions)];
  }
}
